﻿@page "/update-setting"
@using System.Net
@using System.Text
@using Blazor.Core
@using Blazor.Core.Disposable
@using Blazor.Model
@using Newtonsoft.Json
@using Newtonsoft.Json.Converters
@inject IStringLocalizer<UpdateSettingLocalization> Loc
@inject IStringLocalizer<CommonLocalization> CommonLoc
@inject ILogger<UpdateSetting> Logger
@inject HttpClient HttpClient
@inject ModalService ModalService
@inject IMessageService MessageService

<Title Level="1">@Loc["title"]</Title>

<Paragraph>@Loc["description"]</Paragraph>

<Form Model="@_channelInfo" LabelColSpan="4" WrapperColSpan="20" Style="width: 1000px"
      LabelAlign="AntLabelAlignType.Left">
    <FormItem Label="@Loc["channel"]">
        <RadioGroup @bind-value="_channelInfo.Channel" ButtonStyle="RadioButtonStyle.Solid" Disabled="_isLoading"
                    OnChange="OnChannelChange" TValue="Channel">
            @foreach (var c in Enum.GetValues<Channel>())
            {
                <Radio RadioButton Value="@c">@c.ToString()</Radio>
            }
        </RadioGroup>
    </FormItem>
    @if (_isLoading)
    {
        <Spin/>
    }
    else
    {
        <FormItem Label="@Loc["latest_native_version"]">
            <Input Placeholder="@Loc["input_version"]" @bind-Value="@context.LatestNativeVersion"/>
        </FormItem>

        <FormItem Label="@Loc["min_native_version"]">
            <Input Placeholder="@Loc["input_version"]" @bind-Value="@context.MinNativeVersion"/>
        </FormItem>

        <FormItem WrapperColOffset="4">
            <Button HtmlType="submit" Type="@ButtonType.Primary" Danger>@Loc["change_native_version"]</Button>
        </FormItem>

        <FormItem Label="@Loc["raw_resource_version"]">
            <Input Placeholder="@Loc["input_version"]" @bind-Value="@context.RawResourceVersion"/>
        </FormItem>

        <FormItem Label="@Loc["default_resource_version"]">
            <Input Placeholder="@Loc["input_version"]" @bind-Value="@context.DefaultResourceVersion"/>
        </FormItem>

        <FormItem WrapperColOffset="4">
            <Button HtmlType="submit" Type="@ButtonType.Primary" Danger
                    OnClick="OnClickChangeResourceVersion">@Loc["change_resource_version"]</Button>
        </FormItem>
    }
</Form>

<Title Level="2">@Loc["white_list"]</Title>

<Paragraph>@Loc["description_for_white_list"]</Paragraph>

@if (!_isLoading)
{
    @if (_customRules.Any())
    {
        <AntList DataSource="@_customRules">
            <ChildContent Context="item">
                <ListItem>
                    <Space>
                        <SpaceItem>
                            <Text>@item.Key</Text>
                        </SpaceItem>
                        <SpaceItem>
                            <Text>@item.Value.Raw</Text>
                        </SpaceItem>
                        <SpaceItem>
                            <Text>@item.Value.Default</Text>
                        </SpaceItem>
                        <SpaceItem>
                            <Button Danger>@CommonLoc["delete"]</Button>
                        </SpaceItem>
                    </Space>
                </ListItem>
            </ChildContent>
        </AntList>
    }
    else
    {
        <Empty/>
    }

    <Space>
        <SpaceItem>
            <Button OnClick="OnClickAddCustomRule">@CommonLoc["add"]</Button>
        </SpaceItem>

        <SpaceItem>
            <Button OnClick="OnClickDeleteAll">@CommonLoc["delete_all"]</Button>
        </SpaceItem>
    </Space>
}
else
{
    <Spin/>
}


@code {
    public class ChannelInfo
    {
        [JsonProperty("channel")]
        public Channel Channel { get; set; }

        [JsonProperty("latest_native_version")]
        public uint LatestNativeVersion { get; set; }

        [JsonProperty("min_native_version")]
        public uint MinNativeVersion { get; set; }

        [JsonProperty("raw_resource_version")]
        public string RawResourceVersion { get; set; } = string.Empty;

        [JsonProperty("default_resource_version")]
        public string DefaultResourceVersion { get; set; } = string.Empty;

        [JsonProperty("jump_url")]
        public string JumpUrl { get; set; } = string.Empty;

        [JsonProperty("resource_url")]
        public string ResourceUrl { get; set; } = string.Empty;
    }
    
    public class SetResourceVersionRequest
    {
        [JsonProperty("channel")]
        [JsonConverter(typeof(StringEnumConverter))]
        public Channel Channel { get; set; }

        [JsonProperty("ip")]
        [JsonConverter(typeof(IPAddressConverter))]
        public IPAddress IP { get; set; }

        [JsonProperty("raw_version")]
        public string RawVersion { get; set; } = string.Empty;

        [JsonProperty("default_version")]
        public string DefaultVersion { get; set; } = string.Empty;
    }
    
    public class ResourceVersion
    {
        [JsonProperty("raw")]
        public string Raw { get; set; }
        [JsonProperty("default")]
        public string Default { get; set; }
    }

    public class AddWhiteList
    {
        public string IP { get; set; }
        public string Raw { get; set; }
        public string Default { get; set; }
    }
    
    private static readonly Uri UpdateServiceBaseUri = new Uri("http://localhost:10001/");

    private bool _isLoading = false;
    private ChannelInfo _channelInfo = new ChannelInfo();
    private IDictionary<IPAddress, ResourceVersion> _customRules = new Dictionary<IPAddress, ResourceVersion>();
    private IPAddress _myIp = new(new byte[] { 127, 0, 0, 1});

    private IDisposable WithLoading()
    {
        if (_isLoading)
        {
            throw new InvalidOperationException();
        }

        _isLoading = true;
        return new StructDefaultDisposable(() =>
        {
            _isLoading = false;
        });
    }

    private async Task<ChannelInfo?> FetchChannelInfo(Channel channel)
    {
        var response = await HttpClient.GetAsync(new Uri(UpdateServiceBaseUri, $"/channel_info/{channel}"));
        if (!response.IsSuccessStatusCode)
        {
            return null;
        }
        
        var content = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<ChannelInfo>(content);
    }

    private async Task<IDictionary<IPAddress, ResourceVersion>?> FetchCustomRules(Channel channel)
    {
        var response = await HttpClient.GetAsync(new Uri(UpdateServiceBaseUri, $"/get_custom_rules/{channel}"));
        if (response.StatusCode != HttpStatusCode.OK)
        {
            return null;
        }

        var content = await response.Content.ReadAsStringAsync();
        var tmp = JsonConvert.DeserializeObject<Dictionary<string, ResourceVersion>>(content, new IPAddressConverter());
        return tmp.ToDictionary(p => IPAddress.Parse(p.Key), p => p.Value);
    }

    private async Task OnChannelChange(Channel channel)
    {
        using var _ = WithLoading();
        _channelInfo = await FetchChannelInfo(channel) ?? new ChannelInfo();
        _customRules = await FetchCustomRules(channel) ?? new Dictionary<IPAddress, ResourceVersion>();
    }

    protected override async Task OnParametersSetAsync()
    {
        _channelInfo = await FetchChannelInfo(_channelInfo.Channel) ?? new ChannelInfo();
        _customRules = await FetchCustomRules(_channelInfo.Channel) ?? new Dictionary<IPAddress, ResourceVersion>();
        await base.OnParametersSetAsync();
    }

    protected override async Task OnInitializedAsync()
    {
        var response = await HttpClient.GetAsync(new Uri(UpdateServiceBaseUri, "/ip"));
        if (response.IsSuccessStatusCode)
        {
            var content = await response.Content.ReadAsStringAsync();
            if (IPAddress.TryParse(content, out var ip))
            {
                _myIp = ip;
            }
        }
        await base.OnInitializedAsync();
    }

    private async Task OnClickChangeResourceVersion()
    {
        using var _ = WithLoading();
        var request = new SetResourceVersionRequest()
        {
            Channel = _channelInfo.Channel,
            RawVersion = _channelInfo.RawResourceVersion,
            DefaultVersion = _channelInfo.DefaultResourceVersion,
        };

        var uri = new Uri(UpdateServiceBaseUri, "/resource_version");
        var s = JsonConvert.SerializeObject(request);
        var response = await HttpClient.PutAsync(uri, new StringContent(s, Encoding.UTF8, "application/json"));

        if (response.IsSuccessStatusCode)
        {
            _channelInfo = await FetchChannelInfo(_channelInfo.Channel) ?? new ChannelInfo();
            MessageService.Success(CommonLoc["success"].ToString());
        }
        else
        {
            MessageService.Error(CommonLoc["failed"].ToString());
        }
    }

    private RenderFragment ModalFragment(AddWhiteList model)
    {
        return @<Form Model="@model" LabelColSpan="8" WrapperColSpan="16" LabelAlign="AntLabelAlignType.Left">
            <FormItem Label="@Loc["ip"]">
                <Input @bind-value="@context.IP"></Input>
            </FormItem>

            <FormItem Label="@Loc["raw_resource_version"]">
                <Input @bind-value="@context.Raw"></Input>
            </FormItem>

            <FormItem Label="@Loc["default_resource_version"]">
                <Input @bind-value="@context.Default"></Input>
            </FormItem>
        </Form>;
    }

    private async Task OnClickAddCustomRule()
    {
        var modalContext = new AddWhiteList()
        {
            IP = _myIp.ToString(),
        };
        ModalRef modalRef = null;
        modalRef = ModalService.CreateModal(new ModalOptions
        {
            Content = ModalFragment(modalContext),
            OnOk = async e =>
            {
                var request = new SetResourceVersionRequest()
                {
                    Channel = _channelInfo.Channel,
                    IP = IPAddress.Parse(modalContext.IP),
                    RawVersion = modalContext.Raw,
                    DefaultVersion = modalContext.Default,
                };
                
                var uri = new Uri(UpdateServiceBaseUri, new Uri(UpdateServiceBaseUri, "/add_custom_rule"));
                var s = JsonConvert.SerializeObject(request);
                var response = await HttpClient.PutAsync(uri, new StringContent(s, Encoding.UTF8, "application/json"));
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    var customRules = await FetchCustomRules(_channelInfo.Channel);
                    if (customRules is not null)
                    {
                        _customRules.Clear();
                        _customRules = customRules;
                        StateHasChanged();
                        _ = MessageService.Success(CommonLoc["success"].ToString());
                    }
                    else
                    {
                    }
                }
                else
                {
                    _ = MessageService.Error(CommonLoc["failed"].ToString());
                }

                await modalRef.CloseAsync();
            },
            OnCancel = async e =>
            {
                await modalRef.CloseAsync();
            },
        });
    }

    private async Task OnClickDeleteAll()
    {
        if (!_customRules.Any())
        {
            return;
        }

        var response = await HttpClient.DeleteAsync(new Uri(UpdateServiceBaseUri, $"/clear_custom_rules/{_channelInfo.Channel}"));
        if (response.StatusCode == HttpStatusCode.OK)
        {
            _customRules.Clear();
            _ = MessageService.Success(CommonLoc["success"].ToString());
        }
    }

}